/**
 * 
 */
package tr.edu.ozyegin.ccrg.wekaPerformanceRun;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;


/**
 * The singletoned controller class that takes the contoller from the main
 * method and runs the tests
 * 
 * @author im1076
 * @since 3.7.2011
 * @version 0.2
 * @see TestStart
 * 
 */
public final class TestController {
	
 	
	private long testStartTime = 0;
	
	private long testEndTime = 0;
 
	private long totalRunTime = 0;

	private long totalSerialRunTime = 0;

	private long threadRunTime = 0;
 	
	private int testIterationAmount = 0;
	
	private String testType = "";

	private int testAmount = 0;

	private int FinishedTestAmount = 0;
	
	private final static TestController testController = new TestController();

	private WekaRunState testRunState = NoWekaRunState.getSingletonedNoTestState();

	private Properties wekaTestStartProperties = new Properties();
	
	private int IOExceptionAmount = 0;

 
	
	private TestController() {
		// Read properties file.
		try {
		 
			
			// http://jaitechwriteups.blogspot.com/2007/01/how-to-read-properties-file-in-web.html
			  // Get the inputStream
	         InputStream inputStreamWekaTestStart = this.getClass().getClassLoader()
	         	  .getResourceAsStream("wekaTestStart.properties");    

	         

 
	        // load the inputStream using the Properties
	        wekaTestStartProperties.load(inputStreamWekaTestStart);
	        
 			
			
			
			
			
			
			 //http://www.exampledepot.com/egs/java.util/Props.html
 			// FileInputStream fileInputStream = new FileInputStream("C:\\Development\\workspace\\wekaSerialParallelTestRun\\src\\wekaTest.properties");
 			 //wekaPerformanceProperties.load(fileInputStream);

 			
 
			
  
			} catch (Exception e) {
 				System.out.println(e.getMessage());

			}
			
			 
  
		
		
	} // end of constructor

	public static synchronized TestController getSingletonedTestContoller() {

		return testController;

	}

	@Override
	public Object clone() throws CloneNotSupportedException {

		throw new CloneNotSupportedException();
	}

	// logic that will control the test executions.
	// The necessary classes that will be used at this class will be initialized
	// through this method and will be private scoped.
	// The other classes will be initialized when necessary (spring framework is
	// not used...)
	// This method can be synchronized to avoid parallel calls to initialize a test start
	public boolean initializeTest(String _testType, int _testAmount, int _testIterationAmount) {
		
		setTestIterationAmount(_testIterationAmount);
		setTestType(_testType);
		setTestAmount(_testAmount);
		
		if (_testType.equalsIgnoreCase("serial")) {

			if (getTestRunState() instanceof NoWekaRunState) {
				setTestRunState(SerialWekaRunState.getSingletonedSerialTestState());
				WekaRun serialTest = new SerialWekaRun();
				
				// set the timer to calculate the time passed
					
					setTotalSerialRunTime(0);
					setTestStartTime(System.nanoTime()); //start timer
				
					for (int i = 0; i < _testIterationAmount; i++) {
						serialTest.runTest(_testAmount);
						
					}
					
					setTestEndTime(System.nanoTime()); //stop timer
					setTotalRunTime(getTestEndTime()-getTestStartTime()); //Find the difference
					printRunTime();
					
				setTestRunState(NoWekaRunState.getSingletonedNoTestState());

			} else if (getTestRunState() instanceof SerialWekaRunState) {
				System.out
						.println("There is a serial test running at the moment. Exiting...");
				System.exit(0);
			} else if (getTestRunState() instanceof ParallelWekaRunState) {
				System.out
						.println("There is a parallel test running at the moment. Exiting...");
				System.exit(0);
			}

		} else if (_testType.equalsIgnoreCase("parallel")) {
			if (getTestRunState() instanceof NoWekaRunState) {
				setTestRunState(ParallelWekaRunState.getSingletonedParallelTestState());
				WekaRun parallelTest = new ParallelWekaRun();
				
				setTotalRunTime(0);
				setThreadRunTime(0);

				
				
				
				for (int i = 0; i < _testIterationAmount; i++) {

					setFinishedTestAmount(0);
					parallelTest.runTest(_testAmount);
					setTestStartTime(System.nanoTime());
					
					
					while (getFinishedTestAmount() < getTestAmount()){
						System.out.print("");					}
					
					setTestEndTime(System.nanoTime());
					
					setTotalRunTime( getTotalRunTime()+ (getTestEndTime()-getTestStartTime()));
				}
 				
				  
				  printRunTime();
				  
				  // This is for all threads total waiting time
				  //forTotalThreadRunTime = forTotalThreadRunTime + getTotalThreadRunTime();
				  
				
				//TODO the test state should be set to no test state when the parallel tests are finished.
				//This can not be determined here because parallel tests run threaded...
				//setTestRunState(NoTestState.getSingletonedNoTestState());

			} else if (getTestRunState() instanceof SerialWekaRunState) {
				System.out
						.println("There is a serial test running at the moment. Exiting...");
				System.exit(0);
			} else if (getTestRunState() instanceof ParallelWekaRunState) {
				System.out
						.println("There is a parallel test running at the moment. Exiting...");
				System.exit(0);
			}

		}
		
		//When test is ended return from the tests with a positive result
		return true;

	}

	
	private void printRunTime(){
		// TODO Auto-generated method stub
		// Daha düzgün bir görünüm için güncellenecek.
		
		// write the results to file as well: (Beginning part of writing to file)
		try {
			
			
			//String curDir = System.getProperty("user.dir");
 			
			
		    BufferedWriter out = new BufferedWriter(new FileWriter("wekaTestResults.txt", true));
		
		DateFormat dfm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		
		System.out.println("\n"); 		    out.write("\n");

		System.out.println("Test Date : " + dfm.format( (new Date())) ); 		    out.write("Current Date : " + dfm.format( (new Date())) + "\n");
		
		System.out.println("---"); 		    out.write("---" + "\n");

		System.out.println("--- Test Results : ");    out.write("--- Test Results : " + "\n");
		System.out.println( getTestType() + " type test ");    out.write(getTestType() + " type test " + "\n");
		System.out.println( getTestIterationAmount() + " execution iteration for average ");    out.write(getTestIterationAmount() + " execution iteration for average " + "\n");
		System.out.println( getTestAmount() + " process execution");    out.write( getTestAmount() + " process execution" + "\n");
		System.out.println( getIOExceptionAmount() + " IOException at process execution");    out.write( getIOExceptionAmount() + " IOException at process execution" + "\n");
		
		if (getTestType().equalsIgnoreCase("serial")) {
			
			System.out.println("--- Test serial : ");   out.write("--- Test serial : " + "\n");

			System.out.println( getTotalSerialRunTime() + " nano seconds of total run");    out.write(getTotalSerialRunTime() + " nano seconds of total run" + "\n");
			System.out.println( getTotalSerialRunTime() / (getTestAmount()*getTestIterationAmount()) + " nano seconds per execution");   out.write(getTotalSerialRunTime() / (getTestAmount()*getTestIterationAmount()) + " nano seconds per execution" + "\n");
			
		} else {
		
		System.out.println( getTotalRunTime() + " nano seconds of total run");    out.write(getTotalRunTime() + " nano seconds of total run" + "\n");
		System.out.println( getTotalRunTime() / (getTestAmount()*getTestIterationAmount()) + " nano seconds per execution");    out.write(getTotalRunTime() / (getTestAmount()*getTestIterationAmount()) + " nano seconds per execution" + "\n");
		}
		
		if (getTestType().equalsIgnoreCase("parallel")) {
			
			System.out.println("--- Test parallel : ");   out.write("--- Test parallel : " + "\n");

			System.out.println(  "\n");   out.write("\n");
 			System.out.println( getThreadRunTime() + " nano seconds of total run thead waiting time");   out.write( getThreadRunTime() + " nano seconds of total run thead waiting time" + "\n");
 			System.out.println( getThreadRunTime() / (getTestAmount()*getTestIterationAmount()) + " nano seconds per thread process wait time");     out.write(getThreadRunTime() / (getTestAmount()*getTestIterationAmount()) + " nano seconds per thread process wait time" + "\n");
		}
		System.out.println("---");   out.write("---");  out.write("\n");  out.write("\n");  out.write("\n");  out.write("\n");
		
		
	    // end part of writing to file
		out.close();
		} catch (IOException e) {
		}
		
		
		
	} // end of method
	
	
	public Properties getWekaPerformanceProperties (){
		
		return wekaTestStartProperties;
		
		
	} // end of method
	
	
	
	
	
	
	public void setTestRunState(WekaRunState testRunState) {
		this.testRunState = testRunState;
	}

	public WekaRunState getTestRunState() {
		return testRunState;
	}
 

	public void setTestStartTime(long testStartTime) {
		this.testStartTime = testStartTime;
	}

	public long getTestStartTime() {
		return testStartTime;
	}

	public void setTestEndTime(long testEndTime) {
		this.testEndTime = testEndTime;
	}

	public long getTestEndTime() {
		return testEndTime;
	}

	public void setTestIterationAmount(int testIterationAmount) {
		this.testIterationAmount = testIterationAmount;
	}

	public int getTestIterationAmount() {
		return testIterationAmount;
	}

	public void setTestType(String testType) {
		this.testType = testType;
	}

	public String getTestType() {
		return testType;
	}

	public void setTestAmount(int testAmount) {
		this.testAmount = testAmount;
	}

	public int getTestAmount() {
		return testAmount;
	}

	public void setFinishedTestAmount(int finishedTestAmount) {
		FinishedTestAmount = finishedTestAmount;
	}

	public int getFinishedTestAmount() {
		return FinishedTestAmount;
	}
 

 
	public void setTotalRunTime(long totalRunTime) {
		this.totalRunTime = totalRunTime;
	}

	public long getTotalRunTime() {
		return totalRunTime;
	}
	
	public void setThreadRunTime(long threadRunTime) {
		this.threadRunTime = threadRunTime;
	}

	public long getThreadRunTime() {
		return threadRunTime;
	}

	// this method should be called when a execution finishes
	public synchronized void incrementFinishedTestAmount() {
		setFinishedTestAmount(getFinishedTestAmount()+1);
	}

	public synchronized void incrementTotalSerialRuntime(long _duration) {
		// TODO Auto-generated method stub
		setTotalSerialRunTime(getTotalSerialRunTime() + _duration);
	}

	public synchronized void incrementThreadRuntime(long _duration) {
		// TODO Auto-generated method stub
		setThreadRunTime(getThreadRunTime() + _duration);
	}

	public void setTotalSerialRunTime(long totalSerialRunTime) {
		this.totalSerialRunTime = totalSerialRunTime;
	}

	public long getTotalSerialRunTime() {
		return totalSerialRunTime;
	}

	public   void setIOExceptionAmount(int iOExceptionAmount) {
		IOExceptionAmount = iOExceptionAmount;
	}

	public   int getIOExceptionAmount() {
		return IOExceptionAmount;
	}
	 
	public   void incrementIOExceptionAmount() {
		setIOExceptionAmount(getIOExceptionAmount()+1);
	}
	
	
}
